package query

import (
	"errors"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"time"
)

type PageData[T any] struct {
	Page       int64 `json:"page"`
	Size       int64 `json:"size"`
	Count      int64 `json:"count"`      //数据量
	TotalPages int64 `json:"totalPages"` //页数
	Data       []*T  `json:"data"`
}

type Query struct {
	Page   int64             `json:"page,omitempty"`
	Size   int64             `json:"size,omitempty"`
	Filter map[string]bson.M `json:"filter,omitempty"`
	Sort   bson.D            `json:"sort,omitempty"`
}

func (q *Query) Eq(filed string, value any) *Query {
	filterAssignment(q, filed, "$eq", value)
	return q
}
func (q *Query) Ne(filed string, value any) *Query {
	filterAssignment(q, filed, "$ne", value)
	return q
}
func (q *Query) Gt(filed string, value any) *Query {
	filterAssignment(q, filed, "$gt", value)
	return q
}
func (q *Query) Lt(filed string, value any) *Query {
	filterAssignment(q, filed, "$lt", value)
	return q
}
func (q *Query) Ge(filed string, value any) *Query {
	filterAssignment(q, filed, "$gte", value)
	return q
}
func (q *Query) Le(filed string, value any) *Query {
	filterAssignment(q, filed, "$lte", value)
	return q
}
func (q *Query) In(filed string, items ...any) *Query {
	filterAssignment(q, filed, "$in", items)
	return q
}

// PageInfo page, size从1开始
func (q *Query) PageInfo(page, size int64) *Query {
	q.Page = page
	q.Size = size
	return q
}
func (q *Query) ASC(filed string) *Query {
	sortAssignment(q, filed, 1)
	return q
}
func (q *Query) DESC(filed string) *Query {
	sortAssignment(q, filed, -1)
	return q
}

// Convert 数据类型转换 $oid 将字符串转换成 objectId  $date 将字符串转换成time.Time
func (q *Query) Convert() error {
	filter := q.Filter
	for _, value := range filter {
		err := bsonPeek(value)
		if err != nil {
			return err
		}
	}
	return nil
}

func filterAssignment(q *Query, filed, option string, value any) {
	filedInfo, ok := q.Filter[filed]
	if !ok {
		q.Filter[filed] = bson.M{option: value}
		return
	}
	filedInfo[option] = value
}

func sortAssignment(q *Query, filed string, sortType int) {
	if q.Sort == nil {
		q.Sort = bson.D{}
	}
	q.Sort = append(q.Sort, primitive.E{Key: filed, Value: sortType})
}

// 遍历一个bson.M
func bsonPeek(m map[string]interface{}) error {
	var checkArr = make([]bson.M, 0) //待检查的bson
	for key, value := range m {
		subBson, ok := value.(map[string]interface{})
		if !ok { //不是嵌套对象直接结束
			continue
		}
		convertFlag := false
		//检查子对象是否是需要转换的
		for _, tag := range convertList {
			_, exists := subBson[tag]
			if !exists {
				continue
			}
			//存在则需要转换
			convertData, err := bsonMConvert(subBson, tag)
			if err != nil {
				return err
			}
			m[key] = convertData
			convertFlag = true
		}
		if !convertFlag { //说明没有被数据转换,但是 本身还是一个待遍历的bson
			checkArr = append(checkArr, subBson)
		}
	}

	for _, subM := range checkArr {
		err := bsonPeek(subM)
		if err != nil {
			return err
		}
	}
	return nil
}

// 将整个bson.M转换成一个objectId  或者 时间
func bsonMConvert(m bson.M, tag string) (any, error) {
	strData, ok := m[tag].(string)
	if !ok {
		return nil, errors.New("convert fail")
	}
	f := convertMap[tag]
	return f(strData)
}

var convertList = []string{"$oid", "$date"}

var convertMap = map[string]func(str string) (any, error){
	"$oid": func(str string) (any, error) {
		return primitive.ObjectIDFromHex(str)
	},
	"$date": func(str string) (any, error) {
		return time.Parse(time.RFC3339, str)
	},
}
